//
//  CustomDatePicker.swift
//  MoodDiary
//
//  Created by TanJian on 2021/12/19.
//

import UIKit

protocol MDDatePickerDelegate: AnyObject {
    
    func confirmAction(date: String)
    func cancleAction(date: String)
    
}


class CustomDatePicker: UIView {

    enum DateStyle {

        case year_month_day
        case year_month
        case year
    }
    
    private let shadow = UIButton(frame: CGRect(x: 0, y: 0, width: screen_width, height: screen_height))
    private let confirmBtn = UIButton()
    
    var dateType: DateStyle = .year_month_day
    
    
    private var yearsArr = [Int]()
    private var monthsArr = [Int]()
    private var daysArr = [Int]()
    private let datepicker = UIPickerView()
    
    var maxDate: Date? {
        didSet {
//            configLatestDate(date: maxDate!)
        }
    }
    var minDate: Date? {
        didSet {
//            configEarliestDate(date: minDate!)
            
        }
    }
    
    private var earliestDate: (year: Int, month: Int, day: Int) = (0,0,0)
    private var latestDate: (year: Int, month: Int, day: Int) = (0,0,0)
    
    
    private var yearSelected = ""
    private var monthSelected = ""
    private var daySelected = ""
    
    enum position {
        case positionBottom
        case positionCenter
    }

    weak var delegate: MDDatePickerDelegate?
    
    init(frame: CGRect, startTimeStr: String = "1970-01-01", currentPosition: position) {
        
        super.init(frame: frame)
        
        datePickerInit(startTimeStr: startTimeStr)
        
        setupUI(currentPosition: currentPosition)
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func datePickerInit(startTimeStr: String) {
        
        let yearStr = checkYearStrFromTimeStr(timeStr: startTimeStr)
        
        for item in (yearStr.1)...(checkCurrentYearStr().1) {
            yearsArr.append(item)
        }
        
        configEarliestDate(date: configDateStrToDate(yearMonthDayStr: startTimeStr)!)
        
        
        configLatestDate(date: Date())

        yearSelected = checkCurrentYearStr().0
        monthSelected = checkCurrentMonthStr().0
        daySelected = checkCurrentDayStr().0

        createMonthArr()
        createDaysArr()
    }
    
    private func setupUI(currentPosition: position) {
        
        shadow.backgroundColor = UIColor.hexColor(hex: "#000000")
        shadow.alpha = 0.2
        shadow.addTarget(self, action: #selector(cancleBtnAction), for: .touchUpInside)
        addSubview(shadow)
        
        let whiteLine = UIView(frame: CGRect(x: currentPosition == .positionBottom ? 0 : 12, y: currentPosition == .positionBottom ? screen_height - 260 : screen_height / 2 - 130, width: currentPosition == .positionBottom ? screen_width : screen_width - 12 * 2, height: 260))
        whiteLine.backgroundColor = UIColor.white
        if currentPosition == .positionCenter {
            whiteLine.layer.cornerRadius = 10
            whiteLine.layer.masksToBounds = true
        }
        addSubview(whiteLine)
        
        confirmBtn.setTitle("确定", for: .normal)
        confirmBtn.frame = CGRect(x: currentPosition == .positionBottom ? screen_width - 55 : screen_width - 67, y: currentPosition == .positionBottom ? screen_height - 260 : screen_height / 2 - 130, width: 55, height: 45)
        confirmBtn.setTitleColor(UIColor.hexColor(hex: "#1A1917"), for: .normal)
        confirmBtn.addTarget(self, action: #selector(confirmBtnAction), for: .touchUpInside)
        addSubview(confirmBtn)
        
        datepicker.dataSource = self
        datepicker.delegate = self
        
        let frame = CGRect(x: currentPosition == .positionBottom ? 0 : 12, y: currentPosition == .positionBottom ? screen_height - 215 : screen_height / 2 - 130 + 45, width: currentPosition == .positionBottom ? screen_width : screen_width - 12 * 2, height: 215)
        datepicker.frame = frame
        
        
        datepicker.selectRow(yearsArr.count - 1, inComponent: 0, animated: true)
        
        switch dateType {
        case .year_month:
            if checkCurrentYearStr().1 == earliestDate.year {
                datepicker.selectRow(checkCurrentMonthStr().1 - earliestDate.year, inComponent: 1, animated: true)
            }else {
                datepicker.selectRow(checkCurrentMonthStr().1 - 1, inComponent: 1, animated: true)
            }
            
        case .year_month_day:
            if checkCurrentYearStr().1 == earliestDate.year {
                datepicker.selectRow(checkCurrentMonthStr().1 - earliestDate.month, inComponent: 1, animated: true)
            }else {
                datepicker.selectRow(checkCurrentMonthStr().1 - 1, inComponent: 1, animated: true)
            }
            
            if checkCurrentMonthStr().1 == earliestDate.month {
                datepicker.selectRow(checkCurrentDayStr().1 - earliestDate.day, inComponent: 2, animated: true)
            }else {
                datepicker.selectRow(checkCurrentDayStr().1 - 1, inComponent: 2, animated: true)
            }
        default:
            print("")
        }
        
        addSubview(datepicker)
        
        let grayLine = UIView(frame: CGRect(x: currentPosition == .positionBottom ? 0 : 12, y: currentPosition == .positionBottom ? screen_height - 215 : screen_height / 2 - 130 + 45, width: currentPosition == .positionBottom ? screen_width : screen_width - 12 * 2, height: 0.5))
        grayLine.backgroundColor = UIColor.hexColor(hex: "#CCCCCC")
        addSubview(grayLine)
    }
    
    @objc private func cancleBtnAction() {
        print("点击取消")
        if let delegate = delegate {
            switch dateType {
            case .year:
                delegate.cancleAction(date: yearSelected)
            case .year_month:
                delegate.cancleAction(date: "\(yearSelected)-\(monthSelected)")
            case .year_month_day:
                delegate.cancleAction(date: "\(yearSelected)-\(monthSelected)-\(daySelected)")
            }
            
        }
    }
    
    @objc private func confirmBtnAction() {
        print("confirmBtnAction")
        if let delegate = delegate {
            switch dateType {
            case .year:
                delegate.confirmAction(date: yearSelected)
            case .year_month:
                delegate.confirmAction(date: "\(yearSelected)-\(monthSelected)")
            case .year_month_day:
                delegate.confirmAction(date: "\(yearSelected)-\(monthSelected)-\(daySelected)")
            }
            
        }
    }
    
    
    private func gainDaysInMonth( month: Date) -> Int {
        let calendar = NSCalendar.current
        let month = calendar.range(of: .day, in: .month, for: month)
        if let month = month {
            return month.upperBound - 1
        }else {
            return 0
        }
    }
    
    private func convertCurrentDateToStr(dateFormate: String = "yyyy-MM-dd") -> String {
        let formate = DateFormatter()
        formate.dateFormat = dateFormate
        return formate.string(from: Date())
    }
    
    private func convertMonthAgoDateToStr(dateFormate: String = "yyyy-MM-dd") -> String {
        let formate = DateFormatter()
        formate.dateFormat = dateFormate
        
        let lastMonth = Date(timeIntervalSinceNow: -30 * 24 * 60 * 60)
        return formate.string(from: lastMonth)
        
    }
    
    private func convertWeekAgoDateToStr(dateFormate: String = "yyyy-MM-dd") -> String {
        let formate = DateFormatter()
        formate.dateFormat = dateFormate
        
        let lastMonth = Date(timeIntervalSinceNow: -7 * 24 * 60 * 60)
        return formate.string(from: lastMonth)
    }
    
    private func checkCurrentDayStr() -> (String, Int) {
        let formate = DateFormatter()
        formate.dateFormat = "d"
        
        let day = formate.string(from: Date())
        return (day, Int(day)!)
    }
    
    private func checkCurrentMonthStr() -> (String, Int) {
        let formate = DateFormatter()
        formate.dateFormat = "M"
        
        let day = formate.string(from: Date())
        return (day, Int(day)!)
    }
    
    private func checkCurrentYearStr() -> (String, Int) {
        
        let formate = DateFormatter()
        formate.dateFormat = "yyyy"
        
        let day = formate.string(from: Date())
        return (day, Int(day)!)
    }
    
    private func checkYearStrFromTimeStr(timeStr: String) -> (String, Int) {
        
        let formate = DateFormatter()
        formate.dateFormat = "yyyy-MM-dd"
        if let date = formate.date(from: timeStr) {
            formate.dateFormat = "yyyy"
            let day = formate.string(from: date)
            return (day, Int(day)!)
        }else {
            return ("1970", 1970)
        }
    }
    
    private func configDateStrToDate(yearMonthDayStr: String) -> Date? {
        let formate = DateFormatter()
        formate.dateFormat = "yyyy-MM-dd"
        return formate.date(from: yearMonthDayStr)
    }
}

extension CustomDatePicker: UIPickerViewDelegate, UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        switch dateType {
        case .year:
            return 1
        case .year_month:
            return 2
        case .year_month_day:
            return 3
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch dateType {
        case .year:
            
            return yearsArr.count
            
        case .year_month:
            if component == 0 {
                
                return yearsArr.count
            }else {
                return monthsArr.count
            }
        case .year_month_day:
            if component == 0 {
                return yearsArr.count
            }else if component == 1 {
                return monthsArr.count
            }else {
                return daysArr.count
            }
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
        
        var str = ""
        switch dateType {
        case .year:
            str = "\(yearsArr[row])年"
            
        case .year_month:
            if component == 0 {
                str = "\(yearsArr[row])年"
            }else {
                str = "\(monthsArr[row])月"
            }
        case .year_month_day:
            if component == 0 {
                str = "\(yearsArr[row])年"
            }else if component == 1 {
                str = "\(monthsArr[row])月"
            }else {
                str = "\(daysArr[row])日"
            }
        }
    
        return NSAttributedString(string: str, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: UIColor.hexColor(hex:"#1A1917")])
    }
    
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        //返回值
        if component == 0 {
            yearSelected = "\(yearsArr[row])"
            createMonthArr()
            pickerView.reloadComponent(1)
            pickerView.selectRow(0, inComponent: 1, animated: true)
            monthSelected = "\(monthsArr[0])"
            createDaysArr()
            if dateType == .year_month_day {
                pickerView.reloadComponent(2)
                pickerView.selectRow(0, inComponent: 2, animated: true)
                daySelected = "\(daysArr[0])"
            }
        }else if component == 1 {
            monthSelected = "\(monthsArr[row])"
            createDaysArr()
            
            if dateType == .year_month_day {
                pickerView.reloadComponent(2)
                pickerView.selectRow(0, inComponent: 2, animated: true)
                daySelected = "\(daysArr[0])"
            }
        }else {
            daySelected = "\(daysArr[row])"
        }
        
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 40
    }
    
    //MARK: 处理最晚日期 最早日期 很复杂
    private func configLatestDate(date: Date) {
        latestDate = decomposeToYearAndMonthAndDay(Date: date)
    }
    
    private func configEarliestDate(date: Date) {
        earliestDate = decomposeToYearAndMonthAndDay(Date: date)
    }
    
    private func createMonthArr() {
        
        guard let yearInt = Int(yearSelected) else {return}
        monthsArr.removeAll()
        
        if yearInt == earliestDate.year && yearInt == latestDate.year {
            for i in (earliestDate.month)...(latestDate.month) {
                monthsArr.append(i)
            }
        }else {
            if yearInt == earliestDate.year {
                
                for i in (earliestDate.month)...12 {
                    monthsArr.append(i)
                }
            }else if yearInt == latestDate.year {
                for i in 1...(latestDate.month) {
                    monthsArr.append(i)
                }
            }else {
                monthsArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
            }
        }
        
    }
    
    private func createDaysArr() {
        
        guard let monthInt = Int(monthSelected) else {return}
        guard let yearInt = Int(yearSelected) else {return}
        daysArr.removeAll()
        
        let formate = DateFormatter()
        formate.dateFormat = "yyyy-MM"
        let date = formate.date(from: "\(yearInt)-\(monthInt)")
        
        for i in 1...gainDaysInMonth(month: date!) {
            daysArr.append(i)
        }
        
        
        if yearInt == earliestDate.year {
            if monthInt == earliestDate.month {
                daysArr.removeAll()
                for i in (earliestDate.day)...gainDaysInMonth(month: date!) {
                    daysArr.append(i)
                }
            }
            
        }
        if yearInt == latestDate.year{
            
            if monthInt == latestDate.month {
                daysArr.removeAll()
                let upperDay = gainDaysInMonth(month: date!)

                let range = upperDay > latestDate.day ? latestDate.day : upperDay
                for i in 1...range {
                    daysArr.append(i)
                }
            }
        }
        
        if yearInt == earliestDate.year && yearInt == latestDate.year && monthInt == earliestDate.month && monthInt == latestDate.month {
            let upperDay = gainDaysInMonth(month: date!)
            let range = upperDay > latestDate.day ? latestDate.day : upperDay
            daysArr.removeAll()
            for i in (earliestDate.day)...range {
                daysArr.append(i)
            }
        }
        
    }
    
    private func decomposeToYearAndMonthAndDay(Date: Date) -> (year: Int, month: Int, day: Int) {
        
        let formate = DateFormatter()
        formate.dateFormat = "yyyy-MM-dd"
        
        let array = formate.string(from: Date).components(separatedBy: "-")
        guard array.count == 3 else {return (0,0,0)}
        
        return (Int(array[0])!, Int(array[1])!, Int(array[2])!)
    }
    
    
    func reloadyearSelectedMonthAndDay() {
        
        createMonthArr()
        createDaysArr()
        
        datepicker.reloadAllComponents()
        
        switch dateType {
        case .year:
            datepicker.selectRow(0, inComponent: 0, animated: true)
            yearSelected = "\(yearsArr[datepicker.selectedRow(inComponent: 0)])"
        case .year_month:
            datepicker.selectRow(0, inComponent: 0, animated: true)
            datepicker.selectRow(0, inComponent: 1, animated: true)
            yearSelected = "\(yearsArr[datepicker.selectedRow(inComponent: 0)])"
            monthSelected = "\(monthsArr[datepicker.selectedRow(inComponent: 1)])"
        case .year_month_day:
            datepicker.selectRow(0, inComponent: 0, animated: true)
            datepicker.selectRow(0, inComponent: 1, animated: true)
            datepicker.selectRow(0, inComponent: 2, animated: true)
            yearSelected = "\(yearsArr[datepicker.selectedRow(inComponent: 0)])"
            monthSelected = "\(monthsArr[datepicker.selectedRow(inComponent: 1)])"
            daySelected = "\(daysArr[datepicker.selectedRow(inComponent: 2)])"
        }
    }
}
